package web.sso.http.process;

import org.springframework.boot.convert.ApplicationConversionService;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.web.reactive.BindingContext;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import web.sso.http.annotation.framework.Default;
import web.sso.http.annotation.validate.Max;
import web.sso.http.annotation.validate.Min;
import web.sso.http.annotation.validate.Regex;
import web.sso.http.exception.BadRequestException;
import web.sso.http.exception.ValidateException;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * DATE: 2020/1/20
 * TIME: 3:40 下午
 *
 * @author：huangliming
 *
 */
public abstract class BaseProcess implements HandlerMethodArgumentResolver {

    /**
     * 正则对象池
     * 因为正则对象初始化严重耗时
     * 所以采用单例思维初始化
     */
    private static final Map<String, Pattern> PATTERN_POOL = new ConcurrentHashMap<>();

    /**
     * 改方法用于处理没有获取到值的默认逻辑
     * @param methodParameter
     * @param bindingContext
     * @param serverWebExchange
     * @return
     */
    @Override
    public Mono<Object> resolveArgument(
            MethodParameter methodParameter,
            BindingContext bindingContext,
            ServerWebExchange serverWebExchange
    ) {
        // 检查改参数是否是必须参数
        // 如果不是必须参数则需要按照默认值赋值
        return Mono.create(sink -> {
            Default defaultAnnotation = methodParameter.getParameterAnnotation(Default.class);
            if (defaultAnnotation == null) {
                sink.error(new BadRequestException("缺失参数: " + methodParameter.getParameterName()));
                return;
            }
            try {
                Object value = this.convert(defaultAnnotation.value(), methodParameter);
                if (value == null) {
                    sink.error(new BadRequestException("参数错误: " + methodParameter.getParameterName()));
                    return;
                }
                sink.success(this.validate(value, methodParameter));
            } catch (ConversionFailedException e) {
                sink.error(new BadRequestException("参数格式不正确: " + methodParameter.getParameterName()));
            }
        });
    }

    /**
     * 对象转化
     * 将String类型数据转化为方法参数需要的类型
     * @param value
     * @param methodParameter
     * @return
     */
    protected Object convert(String value, MethodParameter methodParameter) {
        return ApplicationConversionService
                .getSharedInstance()
                .convert(value, methodParameter.getParameterType());
    }

    /**
     * 参数校验核心代码
     * @param value
     * @param methodParameter
     * @return
     */
    protected Object validate(Object value, MethodParameter methodParameter) {

        Class<?> parameterType = methodParameter.getParameterType();
        Min min = methodParameter.getParameterAnnotation(Min.class);
        if (min != null) {
            if (!(value instanceof Integer)) {
                throw new ValidateException("Annotation " + Min.class.getName() +
                        " 不允许修饰类型为: " + parameterType.getName() + " 的参数");
            }
            Integer v = (Integer) value;
            if (v < min.value()) {
                throw new BadRequestException("参数不能小于 " + min.value() + " : " + methodParameter.getParameterName());
            }
        }

        Max max = methodParameter.getParameterAnnotation(Max.class);
        if (max != null) {
            if (!(value instanceof Integer)) {
                throw new ValidateException("Annotation " + Max.class.getName() +
                        " 不允许修饰类型为: " + parameterType.getName() + " 的参数");
            }
            Integer v = (Integer) value;
            if (v > max.value()) {
                throw new BadRequestException("参数不能大于 " + max.value() + " : " + methodParameter.getParameterName());
            }
        }

        Regex regex = methodParameter.getParameterAnnotation(Regex.class);
        if (regex != null) {
            if (!(value instanceof String)) {
                throw new ValidateException("Annotation " + Regex.class.getName() +
                        " 不允许修饰类型为: " + parameterType.getName() + " 的参数");
            }
            String v = (String) value;
            Pattern pattern = PATTERN_POOL.get(regex.value());
            synchronized (PATTERN_POOL) {
                if (pattern == null) {
                    pattern = Pattern.compile(regex.value());
                    PATTERN_POOL.put(regex.value(), pattern);
                }
            }
            boolean matches = pattern.matcher(v).matches();
            if (!matches) {
                throw new BadRequestException("参数格式不正确: " + methodParameter.getParameterName());
            }
        }

        return value;
    }
}
